<?php
namespace app\core;
use app\core\Config;

/**
 * 错误处理类
 */
class ErrorHandler {
    /**
     * @var array 错误级别及其对应描述的映射数组
     */
    private static $errorLevels = array(
        E_ERROR => 'Fatal error',
        E_WARNING => 'Warning',
        E_PARSE => 'Parse error',
        E_NOTICE => 'Notice',
        E_CORE_ERROR => 'Core error',
        E_CORE_WARNING => 'Core warning',
        E_COMPILE_ERROR => 'Fatal error',
        E_COMPILE_WARNING => 'Compile warning',
        E_USER_ERROR => 'User error',
        E_USER_WARNING => 'User warning',
        E_USER_NOTICE => 'User notice',
        E_STRICT => 'Strict',
        E_RECOVERABLE_ERROR => 'Recoverable error',
        E_DEPRECATED => 'Deprecated',
        E_USER_DEPRECATED => 'User deprecated',
    );

    /**
     * 处理错误，并输出错误信息
     *
     * @param int $errno 错误级别
     * @param string $errstr 错误消息
     * @param string $errfile 发生错误的文件
     * @param int $errline 发生错误的行数
     * @return bool 总是返回 true 以阻止默认错误处理
     */
    public static function handleError($errno, $errstr, $errfile, $errline) {
        if(self::isErrorTypeExcluded($errno)){
            return;
        }
        $errfile = str_replace(app::_getRootPath(), '', $errfile);
        $errstr = str_replace(app::_getRootPath(), '', $errstr);
        if(Config::get('log', 'enable_error_log')){
            self::saveErrorToFile($errno, $errstr, $errfile, $errline);
        }
        echo "<br />" . PHP_EOL . "<b>" . self::getErrorLevel($errno) . "</b>:  {$errstr} in <b>{$errfile}</b> on line <b>{$errline}</b><br />" . PHP_EOL . PHP_EOL;
        return true;
    }

    /**
     * 在脚本执行结束时处理致命错误
     *
     * @return bool 总是返回 true 以阻止默认错误处理
     */
    public static function handleShutdown() {
        $error = error_get_last();
        if ($error !== null) {
            return self::handleError($error['type'], $error['message'], $error['file'], $error['line']);
        }
    }

    /**
     * 保存错误信息到文件中，按日期存储
     *
     * @param int $errno 错误级别
     * @param string $errstr 错误消息
     * @param string $errfile 发生错误的文件
     * @param int $errline 发生错误的行数
     * @return bool 总是返回 true 以阻止默认错误处理
     */
    private static function saveErrorToFile($errno, $errstr, $errfile, $errline) {
        $errorLevel = self::getErrorLevel($errno);

        $errorMessage = "{$errorLevel}: {$errstr} in {$errfile} on line {$errline}";

        $date = date('Y-m-d H:i:s');
        $filename = date('Y-m-d') . ".txt";
        $logMessage = "[{$date}] {$errorMessage}" . PHP_EOL;

        $logDirectory = Config::get('log', 'error_log_file');
        if (!file_exists($logDirectory)) {
            mkdir($logDirectory, 0777, true);
        }

        file_put_contents($logDirectory . DIRECTORY_SEPARATOR . $filename, $logMessage, FILE_APPEND);

        return true;
    }

    /**
     * 检查指定的错误类型是否在排除列表中
     *
     * @param int $errorType 错误类型常量
     * @return bool 如果错误类型在排除列表中则返回 true，否则返回 false
     */
    private static function isErrorTypeExcluded($errorType)
    {
        $errorReportingLevel = Config::get('log', 'error_reporting_level');
        return ($errorReportingLevel & $errorType) === 0;
    }

    /**
     * 获取错误级别的对应描述
     *
     * @param int $errorConstant 错误级别常量
     * @return string 错误级别的描述，如果未知则返回"Unknown"
     */
    private static function getErrorLevel($errorConstant) {
        if (array_key_exists($errorConstant, self::$errorLevels)) {
            return self::$errorLevels[$errorConstant];
        } else {
            return 'Unknown';
        }
    }
}
